踩坑之路:Activity执行finish以后onStop方法10s以后才调用 |
您所在的位置:网站首页 › activity安卓 finish后重新打开 › 踩坑之路:Activity执行finish以后onStop方法10s以后才调用 |
CDF_cc7d的博客地址: https://www.jianshu.com/u/dd07890ebff0 正文某天,测试提了一个bug,说当前页面关闭了以后回到了上一个页面,但是对应的音乐并没有立刻停止,而是过了一段时间才停止。于是翻阅了一下代码: @Override protected void onStop() { super.onStop(); if (mIsLoading) { mAudioTool.pausePlayAudio(); } }mAudioTool.pausePlayAudio方法是停止播放音频的代码,似乎并没有什么问题。而且这段代码已经运行了好久,之前测试也是通过的,为何这个版本会出现这个问题? 所以首先我debug一下当前页面的onStop方法,结果发现页面关闭的时候onStop方法并没有被执行,然后过了差不多10s左右再被执行,感觉又是踩到了某个坑里面去了,仔细回想了最近改了的代码,问题可能出在了上一个页面的下面这段代码: /** * 开始抖动 */ private fun startShake() { if(mRotateAnim == null){ mRotateAnim = RotateAnimation(-2f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f) } if (mRotateAnim!!.hasStarted() && !mRotateAnim!!.hasEnded()) { //当前动画已经开始并且没有结束 return } //从左向右 mRotateAnim!!.duration = 50 mRotateAnim!!.repeatMode = Animation.REVERSE mRotateAnim!!.repeatCount = Animation.INFINITE val smallAnimationSet = AnimationSet(false) smallAnimationSet.addAnimation(mRotateAnim) cl_wrong_book.startAnimation(smallAnimationSet) }由于设置的repeatCount是INFINITE,所以动画是一直在执行中的。 不过还是有点纳闷,动画跟Activity生命周期有啥关系?为了印证下到底是不是Animation影响的当前页面生命周期调用异常,于是将这段代码进行了删除操作。结果Activity的onStop生命周期还真的就正常执行了。然后自己又做了一个demo,来查看下onStop的执行时间。 (这段是没有动画的) 2020-12-14 12:40:17.334 24575-24575/com.example.demo I/MainActivity: onCreate 2020-12-14 12:40:17.663 24575-24575/com.example.demo I/MainActivity: onStart 2020-12-14 12:40:17.670 24575-24575/com.example.demo I/MainActivity: onResume 2020-12-14 12:40:20.818 24575-24575/com.example.demo I/MainActivity: onPause 2020-12-14 12:40:20.836 24575-24575/com.example.demo I/SecondActivity: onCreate 2020-12-14 12:40:20.857 24575-24575/com.example.demo I/SecondActivity: onStart 2020-12-14 12:40:20.858 24575-24575/com.example.demo I/SecondActivity: onResume 2020-12-14 12:40:21.406 24575-24575/com.example.demo I/MainActivity: onStop 2020-12-14 12:40:22.930 24575-24575/com.example.demo I/SecondActivity: onPause 2020-12-14 12:40:22.936 24575-24575/com.example.demo I/MainActivity: onStart 2020-12-14 12:40:22.937 24575-24575/com.example.demo I/MainActivity: onResume 2020-12-14 12:40:23.439 24575-24575/com.example.demo I/SecondActivity: onStop 2020-12-14 12:40:23.440 24575-24575/com.example.demo I/SecondActivity: onDestroy(这段是加上动画的) 2020-12-14 12:38:06.392 24278-24278/com.example.demo I/MainActivity: onCreate 2020-12-14 12:38:06.563 24278-24278/com.example.demo I/MainActivity: onStart 2020-12-14 12:38:06.565 24278-24278/com.example.demo I/MainActivity: onResume 2020-12-14 12:38:23.940 24278-24278/com.example.demo I/MainActivity: onPause 2020-12-14 12:38:23.964 24278-24278/com.example.demo I/SecondActivity: onCreate 2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onStart 2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onResume 2020-12-14 12:38:24.544 24278-24278/com.example.demo I/MainActivity: onStop 2020-12-14 12:38:28.111 24278-24278/com.example.demo I/SecondActivity: onPause 2020-12-14 12:38:28.117 24278-24278/com.example.demo I/MainActivity: onStart 2020-12-14 12:38:28.118 24278-24278/com.example.demo I/MainActivity: onResume 2020-12-14 12:38:38.153 24278-24278/com.example.demo I/SecondActivity: onStop 2020-12-14 12:38:38.155 24278-24278/com.example.demo I/SecondActivity: onDestroydemo的log也显示了在加上动画以后,确实onStop和onDestroy就会被延迟执行,而且试了多次,发现每次延迟的时间都是10s左右。所以猜测一定存在某种定时执行onStop操作的场景,不然不可能每次都这么凑巧。 既然踩到坑,那么我就得想办法搞清楚为什么会出现这种情况。所以还是跟以前一样,查阅Android源码一探究竟。 finish()操作 /** * Finishes the current activity and specifies whether to remove the task associated with this * activity. */ @UnsupportedAppUsage private void finish(int finishTask) { if (mParent == null) { int resultCode; Intent resultData; synchronized (this) { resultCode = mResultCode; resultData = mResultData; } if (false) Log.v(TAG, "Finishing self: token=" + mToken); try { if (resultData != null) { resultData.prepareToLeaveProcess(this); } if (ActivityTaskManager.getService() .finishActivity(mToken, resultCode, resultData, finishTask)) { mFinished = true; } } catch (RemoteException e) { // Empty } } else { mParent.finishFromChild(this); } // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must // be restored now. if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) { getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE, mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)); } } /** * Call this when your activity is done and should be closed. The * ActivityResult is propagated back to whoever launched you via * onActivityResult(). */ public void finish() { finish(DONT_FINISH_TASK_WITH_ACTIVITY); }Activity的finish方法会调用自身带有参数的finish方法,然后通过Binder会执行ActivityTaskManagerService的finishActivity方法。 @Override public final boolean finishActivity(IBinder token, int resultCode, Intent resultData, int finishTask) { ...代码省略... synchronized (mGlobalLock) { ...代码省略... try { boolean res; final boolean finishWithRootActivity = finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY; if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY || (finishWithRootActivity && r == rootR)) { ...代码省略 } else { res = tr.getStack().requestFinishActivityLocked(token, resultCode, resultData, "app-request", true); if (!res) { Slog.i(TAG, "Failed to finish by app-request"); } } return res; } finally { Binder.restoreCallingIdentity(origId); } } }由于finishTask是DONT_FINISH_TASK_WITH_ACTIVITY类型,所以会走else分支,tr.getStack()得到的是ActivityStack对象,所以接下来执行的就是ActivityStack的requestFinishActivityLocked方法。 /** * @return Returns true if this activity has been removed from the history * list, or false if it is still in the list and will be removed later. */ final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData, String reason, boolean oomAdj, boolean pauseImmediately) { if (r.finishing) { //这个判断条件是为了防止多次进入,做了一道屏障 Slog.w(TAG, "Duplicate finish request for " + r); return false; } mWindowManager.deferSurfaceLayout(); try { //这个方法是为了将finishing设置为true r.makeFinishingLocked(); final TaskRecord task = r.getTaskRecord(); EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, r.mUserId, System.identityHashCode(r), task.taskId, r.shortComponentName, reason); final ArrayList activities = task.mActivities; final int index = activities.indexOf(r); if (index ViewRootImpl#setView. If we are instead reusing // the decor view we have to notify the view root that the // callbacks may have changed. ViewRootImpl impl = decor.getViewRootImpl(); if (impl != null) { impl.notifyChildRebuilt(); } } if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); } else { // The activity will get a callback for this {@link LayoutParams} change // earlier. However, at that time the decor will not be set (this is set // in this method), so no action will be taken. This call ensures the // callback occurs with the decor set. a.onWindowAttributesChanged(l); } } // If the window has already been added, but during resume // we started another activity, then don't yet make the // window visible. } else if (!willBeVisible) { if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set"); r.hideForNow = true; } ...代码省略... r.nextIdle = mNewActivities; mNewActivities = r; if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r); Looper.myQueue().addIdleHandler(new Idler()); }handleResumeActivity的过程自然也不是本文的重点,重点关注最后一句话Looper.myQueue().addIdleHandler(new Idler()),这句话什么作用呢?这里就要重点看下addIdleHandler方法到底添加了什么东西。 MessageQueue.java public void addIdleHandler(@NonNull IdleHandler handler) { if (handler == null) { throw new NullPointerException("Can't add a null IdleHandler"); } synchronized (this) { mIdleHandlers.add(handler); } }很简单的代码,只是将IdleHandler添加到mIdleHandlers的列表中,那么什么时候会使用这个对象呢。查看了mIdleHandlers的调用地方。 发现除了添加和删除IdleHandler对象以外只有MessageQueue的next方法里面有调用的地方。 Message next() { // Return here if the message loop has already quit and been disposed. // This can happen if the application tries to restart a looper after quit // which is not supported. final long ptr = mPtr; if (ptr == 0) { return null; } int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(ptr, nextPollTimeoutMillis); synchronized (this) { // 获取当前时间 final long now = SystemClock.uptimeMillis(); Message prevMsg = null; Message msg = mMessages; if (msg != null && msg.target == null) { //target为空的情况下,才会进入此条件 // Stalled by a barrier. Find the next asynchronous message in the queue. do { prevMsg = msg; msg = msg.next; //遍历messageQueue里面的所有消息,如果存在message是异步的,那么返回给调用者调用。如果不存在异步消息,那么等遍历完退出循环 } while (msg != null && !msg.isAsynchronous()); } if (msg != null) { if (now |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |